#include "netlib/net/event/event_loop.h"

#include <sys/timerfd.h>

#include <iostream>
#include <memory>
#include <thread>
#include <vector>

#include "netlib/net/event/channel.h"
#include "gtest/gtest.h"

using namespace netlib;
using namespace netlib::net;

TEST(EventLoopTest, SimpleTest) {
	netlib::net::EventLoop loop(true);
	std::thread thread([&] { loop.Loop(); });
	sleep(1);
	loop.Quit();
	thread.join();
}

TEST(EventLoopTest, MutiLoopTest) {
	int nums = 4;
	std::vector<std::unique_ptr<EventLoop>> loops(nums);
	std::vector<std::thread> thds;
	thds.reserve(loops.size());
	for (auto& loop : loops) {
		thds.emplace_back([&loop] {
			loop = std::make_unique<EventLoop>(true);
			loop->Loop();
		});
	}
	sleep(1);
	for (auto& loop : loops) {
		loop->Quit();
	}
	for (auto& thd : thds) {
		thd.join();
	}
}

TEST(EventLoopTest, Timerfd) {
	EventLoop loop(true);
	std::thread thd([&loop] { loop.Loop(); });

	auto tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
	Channel ch(&loop, tfd);
	ch.SetReadCallback([&loop](Timestamp) {
		std::cout << "Time is out!" << std::endl;
		loop.Quit();
	});
	ch.EnableReading();

	struct itimerspec howlong;
	memset(&howlong, 0, sizeof(howlong));
	howlong.it_value.tv_sec = 2;
	timerfd_settime(tfd, 0, &howlong, nullptr);

	thd.join();
	ch.DisableAll();
	ch.Remove();
	close(tfd);
}

TEST(EventLoopTest, TimerQueueTest) {
	EventLoop loop(true);
	std::thread thd([&loop] { loop.Loop(); });

	auto start = time(nullptr);

	loop.RunPeriodically(1, [start] {
		std::cout << "[every] start: " << start << ", now: " << time(nullptr) << std::endl;
	});
	loop.RunAfter(1, [start] {
		std::cout << "[1s] start: " << start << ", now: " << time(nullptr) << std::endl;
	});
	loop.RunAfter(2, [start] {
		std::cout << "[2s] start: " << start << ", now: " << time(nullptr) << std::endl;
	});
	loop.RunAfter(5, [start, &loop] {
		std::cout << "[5s] Time is out, start: " << start << ", now: " << time(nullptr)
		          << std::endl;
		loop.Quit();
	});

	thd.join();

	auto end = time(nullptr);
	EXPECT_GE(end - start, 5);
}